<?php

namespace OT;

class DataDictionary
{

    public function __construct($table)
    {
        $this->headers              = $table['header'];
        $this->rows                 = $table['rows'];
        $this->crossingChar         = '+';
        $this->horizontalBorderChar = '-';
        $this->verticalBorderChar   = '|';
        $this->borderFormat         = '%s';
        $this->cellHeaderFormat     = '%s';
        $this->cellRowFormat        = '%s';
        $this->paddingChar          = ' ';
        $this->padType              = STR_PAD_RIGHT;
    }
    /**
     * Renders table to output.
     *
     * Example:
     * +---------------+-----------------------+------------------+
     * | ISBN          | Title                 | Author           |
     * +---------------+-----------------------+------------------+
     * | 99921-58-10-7 | Divine Comedy         | Dante Alighieri  |
     * | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
     * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
     * +---------------+-----------------------+------------------+
     *
     */
    public function render($out = true)
    {
        if (!$this->rows) {
            exit('invalid table content');
        }

        //获得表头行首 +---------------+-----------------------+------------------+
        $output = $this->renderRowSeparator();
        //获取头部输出| ISBN          | Title                 | Author           |
        $output .= $this->renderRow($this->headers, $this->cellHeaderFormat);
        //header存在的话再输出行分割符
        if ($this->headers) {
            $output .= $this->renderRowSeparator();
        }
        //渲染每一行
        foreach ($this->rows as $row) {
            $output .= $this->renderRow($row, $this->cellRowFormat);
        }
        if ($this->rows) {
            $output .= $this->renderRowSeparator();
        }
        if ($out) {
            exit($output);
        } else {
            $this->cleanup();
            return $output;
        }
    }

    public function renderWitoutHeadTail($out = true)
    {
        if (!$this->rows) {
            exit('invalid table content');
        }

        //获取头部输出| ISBN          | Title                 | Author           |
        $output .= $this->renderRow($this->headers, $this->cellHeaderFormat);
        //header存在的话再输出行分割符
        if ($this->headers) {
            $output .= $this->renderRowSeparator();
        }
        //渲染每一行
        foreach ($this->rows as $row) {
            $output .= $this->renderRow($row, $this->cellRowFormat);
        }
        if ($out) {
            print($output);
        } else {
            $this->cleanup();
            return $output;
        }
    }

    //渲染表格行起始分割行
    private function renderRowSeparator()
    {
        if (0 === $count = $this->getNumberOfColumns()) {
            return;
        }

        $markup = $this->crossingChar;
        for ($column = 0; $column < $count; $column++) {
            $markup .= str_repeat($this->horizontalBorderChar, $this->getColumnWidth($column))
            . $this->crossingChar
            ;
        }

        return sprintf($this->borderFormat, $markup) . PHP_EOL;
    }

    /**
     * 渲染表格行.
     *
     * Example: | 9971-5-0210-0 | A Tale of Two Cities  | Charles Dickens  |
     *
     * @param array  $row
     * @param string $cellFormat
     */
    private function renderRow(array $row, $cellFormat)
    {
        if (empty($row)) {
            return;
        }

        $output = $this->renderColumnSeparator();
        for ($column = 0, $count = $this->getNumberOfColumns(); $column < $count; $column++) {
            $output .= $this->renderCell($row, $column, $cellFormat);
            $output .= $this->renderColumnSeparator();
        }
        $output .= $this->writeln('');
        return $output;
    }

    /**
     * 带边距的渲染单元格.
     *
     * @param array   $row
     * @param integer $column
     * @param string  $cellFormat
     */
    private function renderCell(array $row, $column, $cellFormat)
    {
        $cell = isset($row[$column]) ? $row[$column] : '';
        return sprintf(
            $cellFormat,
            $this->str_pad(
                $this->paddingChar . $cell . $this->paddingChar,
                $this->getColumnWidth($column),
                $this->paddingChar,
                $this->padType
            )
        );
    }

    /**
     * 渲染水平列分隔符.
     */
    private function renderColumnSeparator()
    {
        return (sprintf($this->borderFormat, $this->verticalBorderChar));
    }

    /**
     * 获取表格的列数.
     *
     * @return int
     */
    private function getNumberOfColumns()
    {
        if (null !== $this->numberOfColumns) {
            return $this->numberOfColumns;
        }

        $columns   = array(0);
        $columns[] = count($this->headers);
        foreach ($this->rows as $row) {
            $columns[] = count($row);
        }

        return $this->numberOfColumns = max($columns);
    }

    /**
     * 获取列宽.
     *
     * @param integer $column
     *
     * @return int
     */
    private function getColumnWidth($column)
    {
        if (isset($this->columnWidths[$column])) {
            return $this->columnWidths[$column];
        }

        $lengths   = array(0);
        $lengths[] = $this->getCellWidth($this->headers, $column);
        foreach ($this->rows as $row) {
            $lengths[] = $this->getCellWidth($row, $column);
        }

        return $this->columnWidths[$column] = max($lengths) + 2;
    }

    /**
     * 获取单元格宽度.
     *
     * @param array   $row
     * @param integer $column
     *
     * @return int
     */
    private function getCellWidth(array $row, $column)
    {
        if ($column < 0) {
            return 0;
        }

        if (isset($row[$column])) {
            return $this->strlen($row[$column]);
        }

        return $this->getCellWidth($row, $column - 1);
    }

    /**
     * Returns the length of a string, using mb_strlen if it is available.
     *
     * @param string $string The string to check its length
     *
     * @return integer The length of the string
     */
    protected function strlen($string)
    {
        // if (!function_exists('mb_strlen')) {
        return (strlen($string) + mb_strlen($string, 'UTF8')) / 2;
        // }

        // if (false === $encoding = mb_detect_encoding($string)) {
        //     return strlen($string);
        // }

        // return mb_strlen($string, $encoding);
    }

    /**
     * Called after rendering to cleanup cache data.
     */
    private function cleanup()
    {
        $this->columnWidths    = array();
        $this->numberOfColumns = null;
    }

    public function writeln($line = '')
    {
        return $line . PHP_EOL;
    }

    public function str_pad($input, $pad_length, $pad_string, $pad_type)
    {
        $strlen = $this->strlen($input);
        if ($strlen < $pad_length) {
            $difference = $pad_length - $strlen;
            switch ($pad_type) {
                case STR_PAD_RIGHT:
                    return $input . str_repeat($pad_string, $difference);
                    break;
                case STR_PAD_LEFT:
                    return str_repeat($pad_string, $difference) . $input;
                    break;
                default:
                    $left  = $difference / 2;
                    $right = $difference - $left;
                    return str_repeat($pad_string, $left) . $input . str_repeat($pad_string, $right);
                    break;
            }
        } else {
            return $input;
        }
    }

    //生成当前数据库指定表的数据字典（字符串）
    public function generate($tableName = '')
    {
        $this->crossingChar = '|';
        $out_array          = array();
        $output             = '';
        if ($tableName) {
            echo substr($tableName, strlen(C('DB_PREFIX'))) . PHP_EOL;
            $rows  = array();
            $array = M()->query('SHOW FULL COLUMNS FROM ' . $tableName);
            foreach ($array as $key => $value) {
                $rows[] = array($value['Field'], $value['Type'], $value['Comment']);
            }

            $this->headers = array('字段', '类型', '注释');
            $this->rows    = $rows;
            $this->renderWitoutHeadTail();
        }
        echo PHP_EOL;
    }

    public function generateAll()
    {
        $tables = M()->query('SHOW TABLE STATUS;');
        $tables = array_column($tables, 'Name');
        foreach ($tables as $value) {
            $this->generate($value);
            $this->cleanup();
        }
    }
}
